package 注解;

//import java.lang.annotation.Retention;
//import java.lang.annotation.Target;
//import java.lang.reflect.Method;
//import java.util.ArrayList;
//import java.util.List;
//import java.lang.annotation.Annotation;
//import java.lang.annotation.Documented;
//import java.lang.annotation.ElementType;
//import java.lang.annotation.RetentionPolicy;
//import java.lang.annotation.Inherited;
//import java.lang.reflect.AnnotatedType;
//注解是干什么的
//注解本身不提供作用，注解只能是被看作元数据，它不包含任何业务逻辑。注解更像是一个标签，一个声明，表面被注释的这个地方，将具有某种特定的逻辑。
//
//《注解常见的种类》
//常见的注解有三大类：JDK的，自定义的，第三方的（比如框架）
//
//《注解三板斧》
//定义、使用、读取
//
//定义：包括名字，能用到哪些地方，有效期，是否可以被继承
//
//使用：定义好之后在允许的地方使用标注即可
//
//光有前两步，没什么用，如最熟悉的@Override注解，为什么能验证重写是否有效，怎么不是验证重载？spring的@Autowired为什么是注入作用，而不是
//输出一句话？显然，他们在程序中做了实现，使得其注解具有各自的作用，也具有了意义，而赋予灵魂的一步就是读取
//
//读取：让注解发挥作用，给注解注入灵魂
//
//注解前置知识（会的可以跳过）
//首先是元注解，Java中提供了四个：@Documented | @Retention | @Target | @Inherited
//
//分别解释下
//
//@Documented：代表着此注解会被javadoc工具提取成文档
//@Retention： 代表该注解的有效期：
//             SOURCE 表示编译期，如@Override，只做编译时的提示，不会写入字节码中。
//             CLASS表示类加载期，会保存在class文件中，但在运行class文件被丢弃，也是默认值
//             RUNTIME 表示运行期，也是最常用的，可以在代码运行时进行反射执行相关的操作
//@Target：    表示这个注解可以放在哪
//             TYPE：接口、类、枚举、注解
//             FIELD：字段、枚举的常量
//             METHOD：方法
//             PARAMETER：参数
//             CONSTRUCTOR：构造函数
//             LOCAL_VARIABLE：局部变量
//             ANNOTATION_TYPE：注解
//             PACKAGE：包
//@Inherited： 表示子类可以继承该类的注解
//
//举个例子：自己自定义个注解，并赋予它作用，模拟aop功能，在方法前后加入自定义逻辑
//
//-定义注解
 
//方法前执行
//@Retention(RetentionPolicy.RUNTIME)
//@Target(ElementType.METHOD)
//@interface MyBefore {}
//
////主要方法
//@Retention(RetentionPolicy.RUNTIME)
//@Target(ElementType.METHOD)
//@interface MyCore {}
//
// //方法后执行
//@Retention(RetentionPolicy.RUNTIME)
//@Target(ElementType.METHOD)
//@interface MyAfter {}
//
////-使用注解
//class Test {
//     @MyBefore
//     public void init() {
//         System.out.println("初始化。。。");
//     }
//     @MyAfter
//     public void destroy() {
//         System.out.println("销毁。。。");
//     }
//     @MyCore
//     public void core() {
//         System.out.println("核心方法");
//     }
// }
//
////-赋予注解作用
//// 前两步简单，重要的是如何赋予注解作用
//public class 注解作用 {
//     public static void main(String[] args) throws Exception{
//         //获得测试类
//         Class clazz = Test.class;
//         //获得一个实例
//         Test obj = new Test();//clazz.newInstance();
//         //获得Test的所有public方法，将他们存入对应的集合中
//         List<Method> myBeforeList = new ArrayList<>();
//         List<Method> myAfterList = new ArrayList<>();
//         List<Method> myTestList = new ArrayList<>();
//         Method[] methods = clazz.getMethods();
//         for (Method method : methods) {
//             if (method.isAnnotationPresent(MyBefore.class)) {
//                 myBeforeList.add(method);
//                 continue;
//             }
//             if (method.isAnnotationPresent(MyCore.class)) {
//                 myTestList.add(method);
//                 continue;
//             }
//             if (method.isAnnotationPresent(MyAfter.class)) {
//                 myAfterList.add(method);
//                 continue;
//             }
//         }
//         //调用标有MyTest注解的方法
//         for (Method test : myTestList) {
//             //在测试方法前先执行标有MyBefore注解的方法
//             for (Method method : myBeforeList) {
//                 method.invoke(obj);
//             }
//             //调用test方法
//             test.invoke(obj);
//             //在测试方法后执行标有MyAfter注解的方法
//             for (Method method : myAfterList) {
//                 method.invoke(obj);
//             }
//         }
//     }
// }
//输出
// 初始化。。。
// 核心方法
// 销毁。。。
//这只是个举个例子，真实的应用场景绝对不会这么简单
//
//注解的作用：
//1.编译检查
//Annotation具有“让编译器进行编译检查的作用”。
//例如，@SuppressWarnings, @Deprecated和@Override都具有编译检查作用。
//
//2.在反射中使用Annotation
//在反射的Class, Method, Field等函数中，有许多于Annotation相关的接口。这也意味着，我们可以在反射中解析并使用Annotation。
//
//3.根据Annotation生成帮助文档
//通过给Annotation注解加上@Documented标签，能使该Annotation标签出现在javadoc中。
//
//4.能够帮忙查看查看代码
//通过@Override, @Deprecated等，我们能很方便的了解程序的大致结构。另外，我们也可以通过自定义Annotation来实现一些功能。
//
//5.注解处理器
//如果没有用来读取注解的方法和工作，那么注解也就不会比注释更有用处了。使用注解的过程中，很重要的一部分就是创建于使用注解处理器。Java SE5扩展了反射机
//制的API，以帮助程序员快速的构造自定义注解处理器。
//
//6.在框架中的作用
//    在开发Java程序，尤其是Java EE应用的时候，总是免不了与各种配置文件打交道。以Java EE中典型的S(pring)S(truts)H(ibernate)架构来说，
//Spring、Struts和Hibernate这三个框架都有自己的XML格式的配置文件。这些配置文件需要与Java源代码保存同步，否则的话就可能出现错误。而且这
//些错误有可能到了运行时刻才被发现。把同一份信息保存在两个地方，总是个坏的主意。理想的情况是在一个地方维护这些信息就好了。其它部分所需的信息则通
//过自动的方式来生成。JDK 5中引入了源代码中的注解（annotation）这一机制。注解使得Java源代码中不但可以包含功能性的实现代码，还可以添加元数据。
//注解的功能类似于代码中的注释，所不同的是注解不是提供代码功能的说明，而是实现程序功能的重要组成部分。Java注解已经在很多框架中得到了广泛的使用，
//用来简化程序中的配置。
//    因为注解大多都有自己的配置参数，而配置参数以名值对的方式出现，所以从某种角度来说，可以把注解看成是一个XML元素，该元素可以有不同的预定义的属性。
//而属性的值是可以在声明该元素的时候自行指定的。在代码中使用注解，就相当于把一部分元数据从XML文件移到了代码本身之中，在一个地方管理和维护。
//    上面两段话其实已经阐述了java注解的主要作用之一，就是跟踪代码依赖性，实现替代配置文件功能。比较常见的是Spring等框架中的基于注解配置。现在的框
//架很多都使用了这种方式来减少配置文件的数量。基本上秉持着这么一个原则，与具体场景相关的配置应该使用注解的方式与数据关联，与具体场景无关的配置放于
//配置文件中。在另一方面我们还可以在通过设置注解的@Retention 级别在运行时使用反射对不同的注解进行处理。
//
//怎么使用呢？
//例子：先利用反射，获取到注解，然后把利用注解实例化该类的对象
//
//定义一个注解

//@Target(ElementType.TYPE)
//@Retention(RetentionPolicy.RUNTIME)
//@Documented
//@interface FruitProvider {
//    /*供应商编号*/
//    public int id() default -1;
//    /*供应商名称*/
//    public String name() default "";
//    /*供应商地址*/
//    public String address() default "";
//}
//
////对一个类使用注解
//@FruitProvider(id = 1728, name = "why", address = "shenzhen")
//class Apple {
//    private int appleID;
//    private String appleProvidername;
//    private String appleprovideraddress;
////    public Apple() {System.out.println("aaaaa");}
//    public int getAppleID() {
//        return appleID;
//    }
//
//    public void setAppleID(int appleID) {
//        this.appleID = appleID;
//    }
//
//    public String getAppleProvidername() {
//        return appleProvidername;
//    }
//
//    public void setAppleProvidername(String appleProvidername) {
//        this.appleProvidername = appleProvidername;
//    }
//
//    public String getAppleprovideraddress() {
//        return appleprovideraddress;
//    }
//
//    public void setAppleprovideraddress(String appleprovideraddress) {
//        this.appleprovideraddress = appleprovideraddress;
//    }
//}
//
////注解处理器
//class FruitInfoUtil {
//
//    public static Apple getAApple(Class<?> clazz) throws Exception{
//        FruitProvider fb = clazz.getAnnotation(FruitProvider.class);//通过反射获取处理注释
//        System.out.println(fb);
//        //通过newInstance()生成Apple实例，利用反射的结果进行设置
//        Apple ap = (Apple)clazz.newInstance();
////        Apple ap = new Apple();
//        ap.setAppleID(fb.id());
//        ap.setAppleProvidername(fb.name());
//        ap.setAppleprovideraddress(fb.address());
//        return ap;
//    }
//}
//public class 注解作用 {
//
//    public static void main(String[] args) throws  Exception{
//        Apple a = FruitInfoUtil.getAApple(Apple.class);
//        System.out.println("苹果商的ID为："+a.getAppleID());
//        System.out.println("苹果商的名字为："+a.getAppleProvidername());
//        System.out.println("苹果商的地址为:"+a.getAppleprovideraddress());
//    }
//}
//这个实例就很好的说明，现在的框架很多都使用了这种方式来减少配置文件的数量。（因为配置就在代码里面了）。


//自定义注解实例：

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.reflect.Field;
/**
 * 水果名称注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
@interface FruitName {
    String value() default "";
}

/**
 * 水果颜色注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
@interface FruitColor {
    /**
     * 颜色枚举
     */
    public enum Color{ BLUE,RED,GREEN};
    
    /**
     * 颜色属性
     */
    Color fruitColor() default Color.GREEN;

}

/**
 * 水果供应者注解
 */
@Target(FIELD)
@Retention(RUNTIME)
@Documented
@interface FruitProvider {
    /**
     * 供应商编号
     */
    public int id() default -1;
    
    /**
     * 供应商名称
     */
    public String name() default "";
    
    /**
     * 供应商地址
     */
    public String address() default "";
}

/**
 * 注解处理器
 */
class FruitInfoUtil {
    public static void getFruitInfo(Class<?> clazz){
        
        String strFruitName=" 水果名称：";
        String strFruitColor=" 水果颜色：";
        String strFruitProvicer="供应商信息：";
        
        Field[] fields = clazz.getDeclaredFields();//获取(水果)类的字段
        
        for(Field field :fields){
            if(field.isAnnotationPresent(FruitName.class)){//判断字段存在水果名称字段注解
                FruitName fruitName = (FruitName) field.getAnnotation(FruitName.class);//水果类名称字段注解类实例化(实现)
                strFruitName=strFruitName+fruitName.value();//获取字段注解名称
                System.out.println(strFruitName);
            }
            else if(field.isAnnotationPresent(FruitColor.class)){
                FruitColor fruitColor= (FruitColor) field.getAnnotation(FruitColor.class);
                strFruitColor=strFruitColor+fruitColor.fruitColor().toString();
                System.out.println(strFruitColor);
            }
            else if(field.isAnnotationPresent(FruitProvider.class)){
                FruitProvider fruitProvider= (FruitProvider) field.getAnnotation(FruitProvider.class);
                strFruitProvicer=" 供应商编号："+fruitProvider.id()+" 供应商名称："+fruitProvider.name()+" 供应商地址："+fruitProvider.address();
                System.out.println(strFruitProvicer);
            }
        }
    }
}

//import test.FruitColor.Color;

/**
 * 注解使用
 */
class Apple {
    
    @FruitName("Apple")
    private String appleName;
    
    @FruitColor(fruitColor=FruitColor.Color.RED)
    private String appleColor;
    
    @FruitProvider(id=1,name="陕西红富士集团",address="陕西省西安市延安路89号红富士大厦")
    private String appleProvider;
    
    public void setAppleColor(String appleColor) {
        this.appleColor = appleColor;
    }
    public String getAppleColor() {
        return appleColor;
    }
    
    public void setAppleName(String appleName) {
        this.appleName = appleName;
    }
    public String getAppleName() {
        return appleName;
    }
    
    public void setAppleProvider(String appleProvider) {
        this.appleProvider = appleProvider;
    }
    public String getAppleProvider() {
        return appleProvider;
    }
    
    public void displayName(){
        System.out.println("水果的名字是：苹果");
    }
}

/**
 * 输出结果
 */
//public class FruitRun {
public class 注解作用 {
    public static void main(String[] args) {
        FruitInfoUtil.getFruitInfo(Apple.class);
    }
}

//运行结果是：
//
// 水果名称：Apple
// 水果颜色：RED
// 供应商编号：1 供应商名称：陕西红富士集团 供应商地址：陕西省西安市延安路89号红富士大厦